//
//  TCUXMLParser.m
//  TreeMenuTest
//
//  Created by Etienne on 12/07/07.
//  Copyright (c) 2007 Etienne Samson. All rights reserved.
//

#import "CFRuntime_internal.h"

#import "TCUXMLParser.h"

@class TCUCFXMLParser, TCUCFXMLParser__;

id _delegate = nil;

#warning FIXME Map those callbacks to NSXMLParser callbacks
/* The 'thing' we return here show again in addChildCallback (possibly multiple times) when we add
 *the current element childs to it, and it shows again in endXMLStructure when the current element
 * have been completely parsed... This ought to explain why NSXMLParser has so many callbacks, when
 * there's only five here.
 */
void *createXMLStructureCallback(CFXMLParserRef parser, CFXMLNodeRef nodeDesc, void *info)
{
    return objc_msgSend(_delegate,@selector(parser:foundElement:), parser, nodeDesc);
}

void addChildCallback(CFXMLParserRef parser,void *parent,void *child,void *info)
{
    objc_msgSend(_delegate,@selector(parser:addingChild:toParent:), parser, child, parent);
}

void endXMLStructureCallback(CFXMLParserRef parser,void *xmlType,void *info)
{
    objc_msgSend(_delegate,@selector(parser:endedStructure:), parser, xmlType);
}

CFDataRef resolveExternalEntityCallback(CFXMLParserRef parser,CFXMLExternalID *extID,void *info)
{
#warning FIXME How do I get externalEntityName ?
    return (CFDataRef)objc_msgSend(_delegate, @selector(parser:resolveExternalEntityName:systemID:), parser, extID->systemID);
}

Boolean handleErrorCallback(CFXMLParserRef parser, CFXMLParserStatusCode error, void *info)
{
#warning FIXME Convert from CFXMLParserStatusCodes to NSXMLParserErrors
    NSError* nsError = [NSError errorWithDomain:NSXMLParserErrorDomain
                                         code:error
                                     userInfo:nil];
    
    objc_msgSend(_delegate,@selector(parser:parseErrorOccurred:), parser, nsError);
    // We return false to stop parsing.
    return false;
}

@implementation TCUXMLParser

- (CFTypeID) _cfTypeID {
    return CFXMLParserGetTypeID();
}

+ (void) load
{
    _CFRuntimeInitializeClassForBridging(CFXMLParserGetTypeID());
    if (_CFRuntimeSetupBridging(CFXMLParserGetTypeID(), [TCUCFXMLParser class], [TCUCFXMLParser__ class]))
        NSLog(@"Successfully setup bridge for TCUCFXMLParser...");
    else
        NSLog(@"Failed _CFRuntimeSetupBridging()");
}

- (id)initWithContentsOfURL:(NSURL *)url
{
    return [self initWithContentsOfURL:url options:TCUXMLParserNoOptions];
}

- (id)initWithContentsOfURL:(NSURL *)url options:(TCUXMLParserOption)options;
{
    CFXMLParserCallBacks callbacks;
    callbacks.version = 0;
    callbacks.createXMLStructure = createXMLStructureCallback;
    callbacks.addChild = addChildCallback;
    callbacks.endXMLStructure = endXMLStructureCallback;
    callbacks.resolveExternalEntity = resolveExternalEntityCallback;
    callbacks.handleError = handleErrorCallback;
    
    CFXMLParserContext context;
    context.version = 0;
    context.info = NULL;
    context.retain = CFRetain;
    context.release = CFRelease;
    context.copyDescription = CFCopyDescription;
    
    return (TCUXMLParser*)CFXMLParserCreateWithDataFromURL(NULL, (CFURLRef)url, options, kCFXMLNodeCurrentVersion, &callbacks, &context);
}

- (id)initWithData:(NSData *)data
{
    
    return [self initWithData:data options:TCUXMLParserNoOptions];
}

- (id)initWithData:(NSData *)data options:(TCUXMLParserOption)options;
{
    CFXMLParserCallBacks callbacks;
    callbacks.version = 0;
    callbacks.createXMLStructure = createXMLStructureCallback;
    callbacks.addChild = addChildCallback;
    callbacks.endXMLStructure = endXMLStructureCallback;
    callbacks.resolveExternalEntity = resolveExternalEntityCallback;
    callbacks.handleError = handleErrorCallback;
    
    CFXMLParserContext context;
    context.version = 0;
    context.info = NULL;
    context.retain = CFRetain;
    context.release = CFRelease;
    context.copyDescription = CFCopyDescription;
    
    return (TCUXMLParser*)CFXMLParserCreate(NULL, (CFDataRef)data, NULL, options, kCFXMLNodeCurrentVersion, &callbacks, &context);
}

// delegate management. The delegate is not retained.
- (id)delegate
{
    return _delegate;
}

- (void)setDelegate:(id)delegate
{
    _delegate = delegate;
}

- (BOOL)parse
{
    return CFXMLParserParse((CFXMLParserRef)self);
}

- (void)abortParsing
{
    CFXMLParserAbort((CFXMLParserRef)self, NSXMLParserDelegateAbortedParseError, (CFStringRef)@"Error: Delegate Aborted Parsing");
}

- (NSError *)parserError
{
    NSString *error = (NSString*)CFXMLParserCopyErrorDescription((CFXMLParserRef)self);
#warning FIXME Handle converting from CFError strings to NSError ints
    return [NSError errorWithDomain:NSXMLParserErrorDomain code:666 userInfo:nil];
}

@end

// Once a parse has begun, the delegate may be interested in certain parser state. These methods will only return meaningful information during parsing, or after an error has occurred.
@implementation TCUXMLParser (TCUXMLParserLocatorAdditions)
- (NSString *)publicID
{
#warning FIXME How did they done that ?
    return nil;//(NSString*)CFXMLParserGet
}

- (NSString *)systemID
{    
#warning FIXME How did they done that ?
    return nil;//(NSString*)CFXMLParserGet
}

- (int)lineNumber
{
    return CFXMLParserGetLineNumber((CFXMLParserRef)self);
}

- (int)columnNumber
{
    return CFXMLParserGetLocation((CFXMLParserRef)self);
}
@end